// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <stdlib.h>
#include <stdint.h>

#include "runtime_types.h"
#include "mem_alloc.h"
#include "assert.h"
#include "type.h"
#include "map.h"
#include "panic.h"

// This file implements support for reflection on maps.  These
// functions are called from reflect/value.go.

struct mapaccess_ret {
	uintptr_t val;
	_Bool pres;
};

extern struct mapaccess_ret mapaccess(struct __go_map_type *, uintptr_t, uintptr_t) asm("reflect.mapaccess");

struct mapaccess_ret mapaccess(struct __go_map_type *mt, uintptr_t m, uintptr_t key_i)
{
	struct __go_map *map = (struct __go_map *)m;
	void *key;
	const struct __go_type_descriptor *key_descriptor;
	void *p;
	const struct __go_type_descriptor *val_descriptor;
	struct mapaccess_ret ret;
	void *val;
	void *pv;

	__go_assert(mt->__common.__code == GO_MAP);

	key_descriptor = mt->__key_type;
	if (__go_is_pointer_type(key_descriptor))
		key = &key_i;
	else
		key = (void *)key_i;

	if (map == NULL)
		p = NULL;
	else
		p = __go_map_index(map, key, 0);

	val_descriptor = mt->__val_type;
	if (__go_is_pointer_type(val_descriptor)) {
		val = NULL;
		pv = &val;
	} else {
		val = runtime_malloc(val_descriptor->__size);
		pv = val;
	}

	if (p == NULL)
		ret.pres = 0;
	else {
		__builtin_memcpy(pv, p, val_descriptor->__size);
		ret.pres = 1;
	}

	ret.val = (uintptr_t) val;
	return ret;
}

extern void mapassign(struct __go_map_type *, uintptr_t, uintptr_t, uintptr_t, _Bool) asm("reflect.mapassign");

void mapassign(struct __go_map_type *mt, uintptr_t m, uintptr_t key_i, uintptr_t val_i, _Bool pres)
{
	struct __go_map *map = (struct __go_map *)m;
	const struct __go_type_descriptor *key_descriptor;
	void *key;

	__go_assert(mt->__common.__code == GO_MAP);

	if (map == NULL)
		runtime_panicstring("assignment to entry in nil map");

	key_descriptor = mt->__key_type;
	if (__go_is_pointer_type(key_descriptor))
		key = &key_i;
	else
		key = (void *)key_i;

	if (!pres)
		__go_map_delete(map, key);
	else {
		void *p;
		const struct __go_type_descriptor *val_descriptor;
		void *pv;

		p = __go_map_index(map, key, 1);

		val_descriptor = mt->__val_type;
		if (__go_is_pointer_type(val_descriptor))
			pv = &val_i;
		else
			pv = (void *)val_i;
		__builtin_memcpy(p, pv, val_descriptor->__size);
	}
}

extern int32_t maplen(uintptr_t) asm("reflect.maplen");

int32_t maplen(uintptr_t m)
{
	struct __go_map *map = (struct __go_map *)m;

	if (map == NULL)
		return 0;
	return (int32_t) map->__element_count;
}

extern unsigned char *mapiterinit(struct __go_map_type *, uintptr_t) asm("reflect.mapiterinit");

unsigned char *mapiterinit(struct __go_map_type *mt, uintptr_t m)
{
	struct __go_hash_iter *it;

	__go_assert(mt->__common.__code == GO_MAP);
	it = runtime_malloc(sizeof(struct __go_hash_iter));
	__go_mapiterinit((struct __go_map *)m, it);
	return (unsigned char *)it;
}

extern void mapiternext(unsigned char *) asm("reflect.mapiternext");

void mapiternext(unsigned char *it)
{
	__go_mapiternext((struct __go_hash_iter *)it);
}

struct mapiterkey_ret {
	uintptr_t key;
	_Bool ok;
};

extern struct mapiterkey_ret mapiterkey(unsigned char *) asm("reflect.mapiterkey");

struct mapiterkey_ret mapiterkey(unsigned char *ita)
{
	struct __go_hash_iter *it = (struct __go_hash_iter *)ita;
	struct mapiterkey_ret ret;

	if (it->entry == NULL) {
		ret.key = 0;
		ret.ok = 0;
	} else {
		const struct __go_type_descriptor *key_descriptor;
		void *key;
		void *pk;

		key_descriptor = it->map->__descriptor->__map_descriptor->__key_type;
		if (__go_is_pointer_type(key_descriptor)) {
			key = NULL;
			pk = &key;
		} else {
			key = runtime_malloc(key_descriptor->__size);
			pk = key;
		}

		__go_mapiter1(it, pk);

		ret.key = (uintptr_t) key;
		ret.ok = 1;
	}

	return ret;
}

// Make a new map.  We have to build our own map descriptor.

extern uintptr_t makemap(const struct __go_map_type *) asm("reflect.makemap");

uintptr_t makemap(const struct __go_map_type *t)
{
	struct __go_map_descriptor *md;
	unsigned int o;
	const struct __go_type_descriptor *kt;
	const struct __go_type_descriptor *vt;
	struct __go_map *map;
	void *ret;

	// FIXME: Reference count.
	md = (struct __go_map_descriptor *)runtime_malloc(sizeof(*md));
	md->__map_descriptor = t;
	o = sizeof(void *);
	kt = t->__key_type;
	o = (o + kt->__field_align - 1) & ~(kt->__field_align - 1);
	md->__key_offset = o;
	o += kt->__size;
	vt = t->__val_type;
	o = (o + vt->__field_align - 1) & ~(vt->__field_align - 1);
	md->__val_offset = o;
	o += vt->__size;
	o = (o + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
	o = (o + kt->__field_align - 1) & ~(kt->__field_align - 1);
	o = (o + vt->__field_align - 1) & ~(vt->__field_align - 1);
	md->__entry_size = o;

	map = __go_new_map(md, 0);

	ret = runtime_malloc(sizeof(void *));
	__builtin_memcpy(ret, &map, sizeof(void *));
	return (uintptr_t) ret;
}
